package com.irdstudio.tdp.executor.core.plugin.gencode;

import com.irdstudio.tdp.executor.core.plugin.AbstractPlugin;
import com.irdstudio.tdp.executor.core.plugin.common.SSubsInfo;
import com.irdstudio.tdp.executor.core.plugin.common.SSubsInfoDao;
import com.irdstudio.tdp.executor.core.plugin.gencode.dao.AppModelInfoDao;
import com.irdstudio.tdp.executor.core.plugin.gencode.dao.OsrvInoutInfoDao;
import com.irdstudio.tdp.executor.core.plugin.gencode.dao.OsrvIsrvEvalDao;
import com.irdstudio.tdp.executor.core.plugin.gencode.dao.OsrvLinkedInfoDao;
import com.irdstudio.tdp.executor.core.plugin.gencode.dao.OsrvNodeInfoDao;
import com.irdstudio.tdp.executor.core.plugin.gencode.vo.ApiMethod;
import com.irdstudio.tdp.executor.core.plugin.gencode.vo.AppModelInfo;
import com.irdstudio.tdp.executor.core.plugin.gencode.vo.OsrvInoutInfo;
import com.irdstudio.tdp.executor.core.plugin.gencode.vo.OsrvIsrvEval;
import com.irdstudio.tdp.executor.core.plugin.gencode.vo.OsrvLinkedInfo;
import com.irdstudio.tdp.executor.core.plugin.gencode.vo.OsrvNodeInfo;
import com.irdstudio.tdp.executor.core.plugin.util.MyFileUtil;
import com.irdstudio.tdp.executor.core.plugin.util.TmModelUtil;
import com.irdstudio.tdp.executor.core.util.date.CurrentDateUtil;
import com.irdstudio.tdp.executor.rest.init.ExecutorInstInfo;
import com.irdstudio.sdk.beans.freemarker.bo.CodeTemplateDataBo;
import com.irdstudio.sdk.beans.freemarker.bo.TmProjectInfoBo;
import com.irdstudio.sdk.beans.freemarker.util.FreemarkerUtil;
import org.apache.commons.collections.CollectionUtils;

import java.io.File;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 生成API开发模型代码
 * @author ligm
 * @date 2019-10-14
 */
public class ApiServiceModelBuildPlugin extends AbstractPlugin {

	// app_model_info app_model_catalog 外部服务分类代码
	private final String CATALOG_TYPE = "A01";

	private AppModelInfoDao appModelInfoDao;
	private OsrvLinkedInfoDao osrvLinkedInfoDao;
	private OsrvNodeInfoDao osrvNodeInfoDao;
	private OsrvInoutInfoDao osrvInoutInfoDao;
	private OsrvIsrvEvalDao osrvIsrvEvalDao;
	private SSubsInfoDao sSubsInfoDao;

	String subsCode = null;
	SSubsInfo subsInfo;
	/** 读取插件所需要参数 */
	@Override
	protected boolean doReadConfigureFromDB(Connection conn, String szConfIdentify) throws SQLException {
		subsCode = context.getVv().getValue("subs_code");

		sSubsInfoDao = new SSubsInfoDao(conn);
		subsInfo = sSubsInfoDao.querySSubsInfoWithKeys(subsCode);
		return true;
	}

	@Override
	public boolean execute() {
		logger.info("应用{" + subsCode + "}，调用生成API开发模型代码插件,开始生成API开发模型代码的源代码...");
		Connection conn = null;
		try {
			conn = this.getPluginConnection();
			// Dao 初始化
			appModelInfoDao =  new AppModelInfoDao(conn);
			osrvLinkedInfoDao = new OsrvLinkedInfoDao(conn);
			osrvNodeInfoDao = new OsrvNodeInfoDao(conn);
			osrvInoutInfoDao = new OsrvInoutInfoDao(conn);
			osrvIsrvEvalDao = new OsrvIsrvEvalDao(conn);
			sSubsInfoDao = new SSubsInfoDao(conn);
			// 查询所有的外部服务模型
			AppModelInfo appModelInfoParam = new AppModelInfo();
			appModelInfoParam.setAppModelCatalog(CATALOG_TYPE);
			appModelInfoParam.setAppId(subsCode);
			//List<AppModelInfoVO> appModelInfoList = appModelInfoService.queryAppModelInfoListByPage(appInfo.getAppId(), appModelInfoParam);
			List<AppModelInfo> appModelInfoList = appModelInfoDao.queryAppModelInfoList(appModelInfoParam);
			if (CollectionUtils.isNotEmpty(appModelInfoList)) {
				Map<String, CodeTemplateDataBo> controllerMap = new HashMap<>();
				for (AppModelInfo model : appModelInfoList) {
					// 执行模块代码组装插件的方法来组装模块代码
					CodeTemplateDataBo ctdb = controllerMap.get(model.getTableModelCode());
					ctdb = assemble(subsCode, model, ctdb);
					controllerMap.put(model.getTableModelCode(), ctdb);
				}

				if (!controllerMap.isEmpty()) {
					Collection<CodeTemplateDataBo> ctdbList = controllerMap.values();
					for (CodeTemplateDataBo ctdb : ctdbList) {
						// 调用模板渲染工具生成代码
						produceApiFile("XXXController.java",
								"src\\main\\java\\com\\irdstudio\\" + formatClassPath(subsCode) + "\\api\\rest\\",
								subsCode, ctdb);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		} finally {
			if (conn != null) {
				this.closePluginConnection(conn);
			}
		}
		return true;
	}	

	/**
	 * 针对具体的模块信息生成API代码
	 * @param subsCode
	 * @param model
	 * @throws Exception
	 */

	public CodeTemplateDataBo assemble(String subsCode, AppModelInfo model, CodeTemplateDataBo ctdb) throws Exception {

		logger.info("开始组装[{" + model.getAppModelCode() + "-" + model.getAppModelName() + "}]API代码..");
		// 获取相关服务接口

		// 2.获取 osrvLinkedInfo , 构建执行流
		List<OsrvLinkedInfo> links = osrvLinkedInfoDao.queryOsrvLinkedInfoVOListByAppModelId(model.getAppModelId());
		OsrvNodeInfo nodeParam = new OsrvNodeInfo();
		nodeParam.setAppModelId(model.getAppModelId());
		List<OsrvNodeInfo> nodes = osrvNodeInfoDao.queryOsrvNodeInfoList(nodeParam);
		// 计算执行流
		List<OsrvNodeInfo> executeFlow = getExecuteFlow(links, nodes);

		// 3.获取各linked 节点的输入输出信息 osrvIsrvEval
		HashMap<String, List<OsrvInoutInfo>> inData = new HashMap<>();
		HashMap<String, List<OsrvInoutInfo>> outData = new HashMap();
		HashMap<String, List<OsrvIsrvEval>> inEvalData = new HashMap<>();
		HashMap<String, List<OsrvIsrvEval>> outEvalData = new HashMap();
		Map<String, AppModelInfo> serviceMap = new HashMap<>(executeFlow.size());
		Map<String, AppModelInfo> services = new HashMap<>();
		AppModelInfo lastService = null;
		if (CollectionUtils.isNotEmpty(executeFlow)) {
			OsrvIsrvEval inoutEvalParam = new OsrvIsrvEval();
			OsrvInoutInfo inoutParam = new OsrvInoutInfo();

			inoutParam.setAppModelId(model.getAppModelId());
			inoutParam.setIoType(AppModelInfoConstant.IO_TYPE.I.getCode());
			List<OsrvInoutInfo> modelIns = osrvInoutInfoDao.queryOsrvInoutInfoList(inoutParam);
			inData.put(model.getAppModelId(), modelIns);
			inoutParam.setIoType(AppModelInfoConstant.IO_TYPE.O.getCode());
			List<OsrvInoutInfo> modelOuts = osrvInoutInfoDao.queryOsrvInoutInfoList(inoutParam);
			outData.put(model.getAppModelId(), modelOuts);
			for (OsrvNodeInfo node : executeFlow) {

				// 4.获取关联的内部服务信息
				if (node.getNodeType().equals(AppModelInfoConstant.NODE_TYPE.INNER_SERVICE.getCode())) {
					AppModelInfo serviceParam = new AppModelInfo();
					serviceParam.setAppModelId(node.getNodeRefModelId());
					serviceParam.setAppId(subsCode);
					AppModelInfo serviceInfo = appModelInfoDao.queryByPk(serviceParam);
					serviceMap.put(node.getNodeRefModelId(), serviceInfo);
					services.put(serviceInfo.getTableModelCode(), serviceInfo);
					if (serviceInfo.getTableModelCode().equals(model.getTableModelCode())) {
						lastService = serviceInfo;
					}

					inoutEvalParam.setNodeId(node.getNodeId());
					inoutEvalParam.setIoType(AppModelInfoConstant.IO_TYPE.I.getCode());
					List<OsrvIsrvEval> ins = osrvIsrvEvalDao.queryOsrvIsrvEvalList(inoutEvalParam);
					inEvalData.put(node.getNodeId(), ins);

					inoutEvalParam.setIoType(AppModelInfoConstant.IO_TYPE.O.getCode());
					List<OsrvIsrvEval> outs = osrvIsrvEvalDao.queryOsrvIsrvEvalList(inoutEvalParam);
					outEvalData.put(node.getNodeId(), outs);
				}
			}
		}

		// 5.设置模板渲染相关参数
		if (ctdb == null) {
			ctdb = new CodeTemplateDataBo();

			// 1.根据当前用户基本信息及表模型工程信息
			String author = "AutoCoder";
			TmProjectInfoBo tpib = new TmProjectInfoBo();
			tpib.setProjectId(subsInfo.getSubsCode());
			tpib.setProjectCode(subsInfo.getSubsCode());
			tpib.setProjectName(subsInfo.getSubsName());
			ctdb.setTmProjectInfo(tpib);
			ctdb.getEnvBo().setAuthor(author);
			ctdb.getEnvBo().setCurrentDate(CurrentDateUtil.getTodayDate());
			ctdb.getEnvBo().setDesc(model.getAppModelName());

			ctdb.getMapData().put("tableModelCode", model.getTableModelCode());

			ctdb.getMapData().put("methods", new ArrayList<>());
			ctdb.getMapData().put("importServices", new HashMap<>());

		}

		ApiMethod method = new ApiMethod();
		method.setExecuteFlow(executeFlow);
		method.setInData(inData);
		method.setOutData(outData);
		method.setInEvalData(inEvalData);
		method.setOutEvalData(outEvalData);
		method.setServiceData(serviceMap);
		method.setAppModelInfo(model);
		method.setSubsCode(subsCode);
		method.setLastService(lastService);
		List<ApiMethod> methods = (List<ApiMethod>) ctdb.getMapData().get("methods");
		methods.add(method);
		Map<String, AppModelInfo> importServices = (Map<String, AppModelInfo>) ctdb.getMapData().get("importServices");
		importServices.putAll(services);
		return ctdb;

	}

	/**
	 * 获得 外部服务模型的执行流程
	 * @param linkeds
	 * @param nodes
	 * @return
	 */
	private List<OsrvNodeInfo> getExecuteFlow(List<OsrvLinkedInfo> linkeds, List<OsrvNodeInfo> nodes) {
		Map<String, OsrvNodeInfo> nodeMap = new HashMap<>(nodes.size());
		for (OsrvNodeInfo node : nodes) {
			nodeMap.put(node.getNodeId(), node);
		}
		// find header
		int linkedLen = linkeds.size();
		OsrvLinkedInfo head = linkeds.get(0), link = null, tmp = null, next = null;
		for (int i = 1; i < linkedLen; i++) {
			link = linkeds.get(i);
			if (head.getSourceNodeId().equals(link.getTargetNodeId())) {
				linkeds.set(0, link);
				linkeds.set(i, head);
				head = link;
				i = 0;
			}
		}
		// sort linked
		for (int i = 0; i < linkedLen - 1; i++) {
			head = linkeds.get(i);
			for (int j = i + 1; j < linkedLen; j++) {
				next = linkeds.get(j);
				if (head.getTargetNodeId().equals(next.getSourceNodeId())) {
					tmp = linkeds.get(i + 1);
					linkeds.set(i + 1, next);
					linkeds.set(j, tmp);
					break;
				}
			}
		}
		// transfer to list of node
		List<OsrvNodeInfo> executeList = new ArrayList<>(linkedLen + 2);
		for (int i = 0; i < linkedLen; i++) {
			if (i == 0) {
				executeList.add(nodeMap.get(linkeds.get(i).getSourceNodeId()));
			}
			OsrvNodeInfo target = nodeMap.get(linkeds.get(i).getTargetNodeId());
			if (target != null){
				executeList.add(target);
			}
		}
		return executeList;
	}

	/**
	 * 生成API文件
	 * @param templateFile 模板文件
	 * @param codePath 代码路径
	 * @param subsCode 应用信息
	 * @param ctdb 模板文件需要的数据对象
	 * @throws Exception
	 */


	private void produceApiFile(String templateFile,
								String codePath,
								String subsCode,
								CodeTemplateDataBo ctdb) throws Exception {
		String templatePath = ExecutorInstInfo.PROJECT_PATH ;
		String targetPath = ExecutorInstInfo.PROJECT_OUTPUT_PATH + subsCode	+ File.separator + codePath;

		targetPath = pathMatchSystem(targetPath);
		if(!MyFileUtil.checkFileExist(targetPath)) {
			// create directory
			File targetDic = new File(targetPath);
			targetDic.mkdirs();
			//throw new PaasStepException(PaasStepExceptionEnum.TARGET_PATH_NOT_FOUND, "目标路径：" + targetPath + "未找到!");
		}
		String tableModelCode = (String) ctdb.getMapData().get("tableModelCode");
		String modelClassName = TmModelUtil.tableCodeToClassName(tableModelCode);
		String fileNameResult = templateFile.replaceAll(CodeGenerateConstant.TABLE_VAR_NOSPLIT, modelClassName);
		fileNameResult = fileNameResult.replaceAll(CodeGenerateConstant.TABLE_VAR_SPLIT, tableModelCode.replace("_", "-"));
		//logger.info("模板文件：{},代码文件:{}", templatePath + templateFile, targetPath + fileNameResult);
		String outputFileName = targetPath + fileNameResult;
		FreemarkerUtil.produceSingleFile(ctdb, templatePath, templateFile + ".vm", outputFileName);
	}

	/**
	 * 将路径匹配对应系统的目录分隔符
	 * @param path
	 * @return
	 */
	private static String pathMatchSystem(String path) {
		String targetSeparetor = File.separator;
		if (targetSeparetor.equals("\\")) {
			// eg src\main\webapp
			if (path.contains("/")) {
				// path = path.replaceAll("/", "\\\\");
			}
		} else if (targetSeparetor.equals("/")) {
			// eg src/main/webapp
			if (path.contains("\\")) {
				path = path.replaceAll("\\\\", "/");
			}
		}
		return path;
	}

	/**
	 * 格式化应用模板ATC001的源代码路径
	 * @param appCode
	 * @return
	 */
	private static String formatClassPath(String appCode) {
		return appCode.replace(".", "/").replace("-", "/");
	}

}
